#ifndef COVALUE_H
#define COVALUE_H

#define COVALUE_HEAD            \
    ssize_t v_ref;              \
    struct _CoType *v_type;

typedef struct {
    COVALUE_HEAD
} CoValue;

typedef void (*destructor)(CoValue *);
typedef long (*hashfunc)(CoValue *);
typedef int (*equalfunc)(CoValue *, CoValue *);
typedef CoValue *(*getiterfunc) (CoValue *);
typedef CoValue *(*iternextfunc) (CoValue *);
typedef CoValue *(*reprfunc) (CoValue *);

typedef struct _CoType {
    const char *tp_name;

    destructor tp_dealloc;

    hashfunc tp_hash;
    equalfunc tp_equal;

    reprfunc tp_str;

    /* Iterators */
	getiterfunc tp_iter;
	iternextfunc tp_iternext;

#ifdef CO_COUNT_ALLOCS
	/* these must be last and never explicitly initialized */
	int tp_allocs;
	int tp_frees;
	int tp_maxalloc;
	struct _CoType *tp_next;
#endif
} CoType;

#define CO_CHECK_TYPE(vp, tp)   ((vp)->v_type == (tp))

#ifdef CO_COUNT_ALLOCS
void inc_count(CoType *tp);
void dec_count(CoType *tp);
#define _CO_INC_TPALLOCS(vp)    inc_count((vp)->v_type)
#define _CO_INC_TPFREES(vp)     dec_count((vp)->v_type)
#define _CO_COUNT_ALLOCS_COMMA  ,
#else
#define _CO_INC_TPALLOCS(vp)
#define _CO_INC_TPFREES(vp)
#define _CO_COUNT_ALLOCS_COMMA
#endif

#define _CO_NewRef(vp)      (                           \
        _CO_INC_TPALLOCS(vp) _CO_COUNT_ALLOCS_COMMA     \
        (vp)->v_ref = 1)
#define _CO_Dealloc(vp)     (                           \
        _CO_INC_TPFREES(vp) _CO_COUNT_ALLOCS_COMMA      \
        (*(vp)->v_type->tp_dealloc)((CoValue *)(vp)))

#define CO_INCREF(vp)       ((vp)->v_ref++)
#define CO_DECREF(vp)           \
    if (--(vp)->v_ref == 0)     \
        _CO_Dealloc(vp)

#define CO_XINCREF(vp)      if ((vp) == NULL) ; else CO_INCREF(vp)
#define CO_XDECREF(vp)      if ((vp) == NULL) ; else CO_DECREF(vp)

#define COVALUE_INIT(vp, tp)    \
    ( (vp)->v_type = (tp), _CO_NewRef((CoValue *)vp), (vp) )

void CoValue_Free(void *self);
long CoValue_Hash(CoValue *self);
int CoValue_Equal(CoValue *va, CoValue *vb);
CoValue *CoValue_SelfIter(CoValue *self);

/* Iterators */
CoValue *CoValue_GetIter(CoValue *self);
CoValue *CoIter_Next(CoValue *iter);

CoValue *CoValue_Str(CoValue *self);

/* lvalue */
typedef struct {
    COVALUE_HEAD
    CoValue **l_ptr;
} CoLValue;

extern CoType CoLValue_Type;

#define COLVALUE_CHECK(self)   CO_CHECK_TYPE(self, &CoLValue_Type)

CoValue *CoLValue_New(CoValue **ptr);

#define CoLValue_GET_LVALUE(self)   (((CoLValue *)self)->l_ptr)

#endif
